package model;

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import controler.CompleteLogger;
import controler.Helper;

/**
 * @author megea
 */
public class Valve extends Recognizable {

	private static final Double STEP = Double.valueOf(0.25);
	private static final Double FULL_OPEN = Double.valueOf(1);
	private static final Double FULL_CLOSE = Double.valueOf(0);
	/**
	 * @uml.property name="opening"
	 */
	private Double opening = Double.valueOf(0);
	/**
	 * @uml.property name="origin"
	 * @uml.associationEnd
	 */
	private final Cistern origin;
	/**
	 * @uml.property name="destiny"
	 * @uml.associationEnd
	 */
	private final Cistern destiny;
	/**
	 * @uml.property name="filter"
	 * @uml.associationEnd
	 */
	private Filter filter;

	private final Timer timer = new Timer("Valve");
	private final Random randomGenerator = new Random();
	private int failLikelihood = Constants.FAIL_LIKELIHOOD_VALVE;

	public Valve(Cistern origin, Cistern destiny, Integer id) {
		super(id);
		this.origin = origin;
		this.destiny = destiny;
		Filler filler = new Filler();
		timer.schedule(filler, 0, Constants.SECONDS);
		this.filter = new Filter(0);
	}

	public Valve(Cistern origin, Cistern destiny, Filter filter, Integer id) {
		this(origin, destiny, id);
		this.filter = filter;
	}

	/**
	 * @return The valve opening as a Double between 0 and 1. 0 means the valve
	 *         is closed 1 means the valve is fully open.
	 * @uml.property name="opening"
	 */
	public Double getOpening() {
		return opening;
	}

	public Boolean isOpen() {
		return !opening.equals(Valve.FULL_CLOSE);
	}

	public void openStep() throws Exception {
		Helper.pause();
		if (randomGenerator.nextInt(100) < failLikelihood) {
			throw new Exception("The " + this.toString() + " is stuck");
		}
		if ((opening + Valve.STEP) < Valve.FULL_OPEN) {
			opening += Valve.STEP;
			CompleteLogger.getLogger().info(this.toString() + " opening, is open " + (this.getOpening() * 100) + "% now");
		} else {
			opening = Valve.FULL_OPEN;
			CompleteLogger.getLogger().info(this.toString() + " is fully open");
		}
	}

	public void closeStep() throws Exception {
		Helper.pause();
		if (randomGenerator.nextInt(100) < failLikelihood) {
			throw new Exception("The " + this.toString() + " is stuck");
		}
		if ((opening - Valve.STEP) > Valve.FULL_CLOSE) {
			opening -= Valve.STEP;
			CompleteLogger.getLogger().info(this.toString() + " closing, still open " + (this.getOpening() * 100) + "%");

		} else {
			opening = Valve.FULL_CLOSE;
			CompleteLogger.getLogger().info(this.toString() + " is fully closed");

		}
	}

	public Double getContentAmount() {
		return opening * Constants.AMOUNT_LIQUID_PIPE;
	}

	private class Filler extends TimerTask {

		@Override
		public void run() {
			Double amount;
			if (destiny.getRest() >= (getContentAmount() * filter.getPass())) {
				amount = origin.emptyStep(getContentAmount()) * filter.getPass();
			} else {
				amount = origin.emptyStep(destiny.getRest() * filter.getPass());
			}
			destiny.addContent(amount);
		}

	}

	public boolean isFullyOpen() {
		return opening.doubleValue() == Valve.FULL_OPEN.doubleValue();
	}

	public boolean isFullyClose() {
		return opening.doubleValue() == Valve.FULL_CLOSE.doubleValue();

	}

	public void amortize() {
		failLikelihood = failLikelihood + 10;
		if (failLikelihood > Constants.MAX_LIKELIHOOD) {
			failLikelihood = Constants.MAX_LIKELIHOOD;
		}
	}

	public void repair() {
		failLikelihood = failLikelihood - 10;
		if (failLikelihood < Constants.MIN_LIKELIHOOD) {
			failLikelihood = Constants.MIN_LIKELIHOOD;
		}
	}
}
